package cn.bluemobi.platform.utils.wechatPay;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.xml.sax.InputSource;

import cn.bluemobi.platform.core.utils.PropertiesUtils;

/**
 * 微信支付
 * 
 * @author Chenwq
 * 
 */
public class WechatPayUtilWeb {
    public static String getCertificate(String key, String filePath) {
        Properties pro = new Properties();
        try {
            pro.load(WechatPayUtilWeb.class.getResourceAsStream(filePath));
            return pro.getProperty(key);
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 验证证书 Description: <br/>
     * 
     * @author lvxh
     * @param request
     * @return
     * @throws Exception
     */
    private static CloseableHttpClient verificationCertificate(HttpServletRequest request) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 存放微信证书目录
        InputStream instream = WechatPayUtilWeb.class.getResourceAsStream("/conf/apiclient_cert_public.p12");// 放退款证书的路径
        try {
            keyStore.load(instream, PropertiesUtils.getProperty("WECHAT_PUBLIC_MERCHANT_ID").toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, PropertiesUtils.getProperty("WECHAT_PUBLIC_MERCHANT_ID").toCharArray())
                .build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        return httpclient;
    }

    /**
     * 申请退款 Description: <br/>
     * 
     * @author lvxh
     * @param orderSn 商户订单号
     * @param transactionId 微信订单号
     * @param totalFee 总金额
     * @param refundFee 总金额
     * @param outRefundNo 商户退款单号
     * @return
     * @throws Exception
     */
    public static Map<String, String> wechatRefund(String orderSn, String transactionId, String totalFee,
            String refundFee, String outRefundNo, HttpServletRequest request) {
        Map<String, String> paramMap = new LinkedHashMap<String, String>();
        try {
            CloseableHttpClient httpclient = verificationCertificate(request);
            // 公众账号ID
            paramMap.put("appid", PropertiesUtils.getProperty("WECHAT_PUBLIC_APPID"));
            // 币种
            paramMap.put("fee_type", "CNY");
            // 商户号
            paramMap.put("mch_id", PropertiesUtils.getProperty("WECHAT_PUBLIC_MERCHANT_ID"));
            // 随机数
            paramMap.put("nonce_str", getNonceStr());
            // 操作员
            paramMap.put("op_user_id", PropertiesUtils.getProperty("WECHAT_PUBLIC_MERCHANT_ID"));
            // 商户退款单号
            paramMap.put("out_refund_no", outRefundNo);
            // 商户订单号
            paramMap.put("out_trade_no", orderSn);
            // 退款金额
            paramMap.put("refund_fee", refundFee);
            // 总金额
            paramMap.put("total_fee", totalFee);
            // 微信订单号
            paramMap.put("transaction_id", transactionId);

            System.out.println(serialize(paramMap));
            // 签名
            String sign = WeChatMd5.MD5Encode(serialize(paramMap));
            paramMap.put("sign", sign.toUpperCase());
            String requestXML = serializeXML(paramMap);
            return submitUrl(requestXML, PropertiesUtils.getProperty("WECHAT_PUBLIC_REFUNND_URL"), httpclient);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return paramMap;
    }

    /**
     * java main方法 数据提交 提交到后台
     * 
     * @param contentData
     * @return 返回报文 map
     */
    private static Map<String, String> submitUrl(String xmlParams, String requestUrl, CloseableHttpClient httpclient) {
        Map<String, String> map = new HashMap<String, String>();
        HttpPost pmethod = new HttpPost(requestUrl); // 设置响应头信息
        pmethod.addHeader("Connection", "keep-alive");
        pmethod.addHeader("Accept", "*/*");
        pmethod.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        pmethod.addHeader("Host", "api.mch.weixin.qq.com");
        pmethod.addHeader("X-Requested-With", "XMLHttpRequest");
        pmethod.addHeader("Cache-Control", "max-age=0");
        pmethod.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
        try {
            pmethod.setEntity(new StringEntity(xmlParams, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(pmethod);
            String responseStr = EntityUtils.toString(response.getEntity(), "UTF-8");

            StringReader read = new StringReader(responseStr);
            // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
            InputSource source = new InputSource(read);
            // 创建一个新的SAXBuilder
            SAXBuilder sb = new SAXBuilder();
            // 通过输入源构造一个Document
            Document doc = sb.build(source);
            // 取的根元素
            Element root = doc.getRootElement();
            // 得到根元素所有子元素的集合
            List<?> jiedian = root.getChildren();
            for (int i = 0; i < jiedian.size(); i++) {
                Element et = (Element) jiedian.get(i);// 循环依次得到子元素
                map.put(et.getName(), et.getText());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 生成随机数 nonce_str
     */
    private static String getNonceStr() {
        String letter[] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
                "S", "T", "U", "V", "W", "S", "Y", "Z" };
        String nonce_str = "";
        for (int i = 0; i < 20; i++) {
            int num = new Random().nextInt(26);
            nonce_str += letter[num];
        }
        return nonce_str;
    }

    /**
     * 序列化参数
     * 
     * @param params 参数map
     * @return 序列化值
     */
    private static String serialize(Map<String, String> params) {
        StringBuffer sb = new StringBuffer("");
        for (Iterator<String> iterator = params.keySet().iterator(); iterator.hasNext();) {
            String key = iterator.next();
            sb.append(key).append("=").append(params.get(key)).append(iterator.hasNext() ? "&" : "");
        }
        sb.append("&key=").append(PropertiesUtils.getProperty("WECHAT_PUBLIC_KEY"));
        return sb.toString();
    }

    private static String serializeXML(Map<String, String> params) {
        StringBuffer sb = new StringBuffer("<xml>");
        for (Iterator<String> iterator = params.keySet().iterator(); iterator.hasNext();) {
            String key = iterator.next();
            sb.append("<").append(key).append(">").append(params.get(key)).append("</").append(key).append(">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点，则此节点的值是子节点的xml数据。
     * 
     * @param strxml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static Map<String, String> doXMLParse(String strxml) throws Exception {
        if (null == strxml || "".equals(strxml)) {
            return null;
        }

        Map<String, String> m = new HashMap<String, String>();
        InputStream in = new ByteArrayInputStream(strxml.getBytes());
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List<?> list = root.getChildren();
        Iterator<?> it = list.iterator();
        while (it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List<?> children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }

            m.put(k, v);
        }

        // 关闭流
        in.close();
        return m;
    }

    /**
     * 获取子结点的xml
     * 
     * @param children
     * @return String
     */
    private static String getChildrenText(List<?> children) {
        StringBuffer sb = new StringBuffer();
        if (!children.isEmpty()) {
            Iterator<?> it = children.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List<?> list = e.getChildren();
                sb.append("<" + name + ">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }
        return sb.toString();
    }

    /**
     * 验证签名是否正确
     */
    public static boolean validateSign(Map<String, String> map) {
        String sign1 = map.remove("sign");
        List<String> keys = new ArrayList<String>();
        keys.addAll(map.keySet());
        Collections.sort(keys);
        Map<String, String> newMap = new LinkedHashMap<String, String>();
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            newMap.put(key, map.get(key));
        }
        String sign = WeChatMd5.MD5Encode(serialize(newMap)).toUpperCase();
        return sign1.equals(sign);
    }
}
